/**  
 * Copyright (c) 2010 University of Pennsylvania.
 *     All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS
 *  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied.  See the License for the specific language
 *  governing permissions and limitations under the License.
 *
 */
 
#define STATE_FILE        "state/node%d.txt"      // complete state snapshots

#define MSGSENT_LOG       "msg_sent.log"            // logs of sent, received and confirmed messages
#define MSGSENTID_LOG     "logs/msg_sent%d.log"   // msg - physical radio messages
#define MSGRCV_LOG        "msg_rcv.log"             // cmd - logical messages
#define MSGRCVID_LOG      "logs/msg_rcv%d.log"
#define CMDSENT_LOG       "cmd_sent.log"
#define CMDSENTID_LOG     "logs/cmd_sent%d.log"
#define CMDDROP_LOG       "cmd_drop.log"
#define CMDDROPID_LOG     "logs/cmd_drop%d.log"
#define CMDSENTCONF_LOG   "cmd_sentconf.log"
#define CMDSENTCONFID_LOG "logs/cmd_sentconf%d.log"
#define CMDRCV_LOG        "cmd_rcv.log"
#define CMDRCVID_LOG      "logs/cmd_rcv%d.log"
#define CMDHISENT_LOG     "cmdhi_sent.log"
#define CMDHISENTID_LOG   "logs/cmdhi_sent%d.log"
#define CMDHIRCV_LOG      "cmdhi_rcv.log"
#define CMDHIRCVID_LOG    "logs/cmdhi_rcv%d.log"
#define ROUTESTATS_FILE       "%s/stats%d.txt"      // route statistics and established paths
#define ROUTECOMPLETED_FILE   "%s/completed%d.txt"
#define ROUTESOURCEROUTE_FILE "%s/sourceroute%d.txt"
#define ROUTEREQUEST_FILE     "%s/request%d.txt"
#define ROUTEUNREACHABLE_FILE "%s/unreachable%d.txt"

#define FORWARDREQUEST_FILE     "%s/forward_request%d.txt" // completed paths during forwarding
#define FORWARDCOMPLETED_FILE   "%s/forward_completed%d.txt"
#define FORWARDUNREACHABLE_FILE "%s/forward_unreachable%d.txt"
#define MULTICASTREQUEST_FILE   "%s/multicast_request%d.txt"
#define MULTICASTCOMPLETED_FILE "%s/multicast_completed%d.txt"
#define REPAIRFORWARD_FILE      "%s/repairforward_log%d.txt"

#define JOINPAIR_FILE              "%s/join_pairs%d.txt"
#define JOINNODE_FILE              "%s/join_nodes%d.txt"
#define COORDCOST_FILE             "%s/coord_cost%d.txt"
#define COORDDECISION_FILE         "%s/coord_decision%d.txt"
#define SELEST_FILE                "%s/sel_est%d.txt"
#define QUERYSAMPLELOG_FILE        "%s/sample_log%d.txt"
#define QUERYJOININLOG_FILE        "%s/join_inlog%d.txt"
#define QUERYJOINOUTLOG_FILE       "%s/join_outlog%d.txt"
#define QUERYJOINCOSTMODELLOG_FILE "%s/join_costmodel_log%d.txt"

#ifdef TOSSIM
typedef uint32_t statsl_t;
typedef uint16_t stats_t;
#else
typedef uint16_t statsl_t;
typedef uint8_t stats_t;
#endif

statsl_t total_pathlen, msgsent_total, msgsent_extern;
statsl_t msgsent_route, msgsent_forward, msgsent_conf, msgsent_split, msgsent_splitforward;
statsl_t bytesent_total, bytesent_payload, bytesent_totalnoconf, bytesent_route, bytesent_forward;
statsl_t msgrcv_total, msgrcv_crcerror, msgrcv_route, msgrcv_forward, msgrcv_conf, msgrcv_split;
statsl_t bytercv_total, bytercv_route, bytercv_forward, bytercv_payload;
statsl_t msggroup_sent;
stats_t maxusage_group, maxusage_inputbuffer, maxusage_outputbuffer, maxusage_mergebuffer,
  maxusage_payloadbuffer;

void init_mlogs()
{
  total_pathlen = 0; msgsent_total = 0; msgsent_route = 0; msgsent_conf = 0;
  msgsent_split = 0; msgsent_splitforward = 0; msgsent_forward = 0;
  bytesent_route = 0; bytesent_forward = 0; bytesent_total = 0; bytesent_payload = 0;
  bytesent_totalnoconf = 0;
  msgrcv_total = 0; msgrcv_route = 0; msgrcv_forward = 0; msgrcv_conf = 0; msgrcv_split = 0;
  msgrcv_crcerror = 0;
  bytercv_total = 0; bytercv_route = 0; bytercv_forward = 0; bytercv_payload = 0;
  msggroup_sent = 0; msgsent_extern = 0;
  maxusage_group = 0; maxusage_inputbuffer = 0; maxusage_outputbuffer = 0; maxusage_mergebuffer = 0;
  maxusage_payloadbuffer = 0;
  
#ifdef USE_MEMFILES
  init_filebuffer(0);
#endif  
}

#ifdef TOSSIM
#define CONFIG_TREEDIR    config.tree_dir
#define CONFIG_VALUEDIR   config.value_dir
#define CONFIG_JOINDIR    config.join_dir
#define CONFIG_SAMPLEDIR  config.sample_dir
#define CONFIG_ROUTEDIR   config.route_dir
#else
#ifdef USE_MEMFILES
#define CONFIG_TREEDIR    "trees"
#define CONFIG_VALUEDIR   "values"
#define CONFIG_JOINDIR    "join"
#define CONFIG_SAMPLEDIR  "samples"
#define CONFIG_ROUTEDIR   "route"

void init_filebuffer(int file_size)
{
  file_buffer = (file_size > 0) ? (uint8_t *) malloc(file_size) : NULL;
  filebuffer_alloc = file_size;
  filebuffer_pos = 0;
  filebuffer_size = 0;
}

void reset_filebuffer()
{
  filebuffer_pos = 0;
  filebuffer_size = 0;
  filebuffer_alloc = 0;
  if (file_buffer) 
  {
    free(file_buffer);
    file_buffer = NULL;
  }
}

int fprintf(FILE *f, const char *fmt, ...) 
{
  va_list argptr;
  int output_len;

  if (f == NULL || file_buffer == NULL || filebuffer_alloc == 0 || filebuffer_pos >= filebuffer_alloc) 
    return -1;
    
  va_start(argptr, fmt);
  output_len = vsnprintf(&file_buffer[filebuffer_pos], filebuffer_alloc-filebuffer_pos, fmt, argptr);
  va_end(argptr);  
  
  if (output_len > 0)
    filebuffer_pos += output_len;
  filebuffer_size = filebuffer_pos;  
  return output_len;  
}

int	fscanf(FILE *f, const char *fmt, ...) 
{
  return -1;
}

FILE *file_open(char *name, char *args) 
{
  FILE *f = NULL;
  
  if (file_buffer == NULL || filebuffer_alloc == 0)
    return NULL;
    
  filebuffer_pos = 0; 
  filebuffer_size = 0;
  f = (FILE *) malloc(sizeof(FILE));
  fprintf(f, "%s\n", name);
  
  return f;
}

int fclose(FILE *f)
{
  return 0;
}

#endif
#endif

#if defined(TOSSIM) || defined(USE_MEMFILES) 
void summary_store(SUMMARY *s, FILE *f)
{
  int i;

  if (s == NULL)
    return;

  fprintf(f, "%d %d %d ", s->type, s->options, s->count);
  for (i = 0; i < SUMMARY_LEN; i++)
    fprintf(f, "%d ", s->summary[i]);
  fprintf(f, "\n");
}

void summary_load(SUMMARY *s, FILE *f)
{
  int i;

  if (s == NULL)
    return;

  fscanf(f, "%hhu %hhu %hu ", &s->type, &s->options, &s->count);
  for (i = 0; i < SUMMARY_LEN; i++)
    fscanf(f, "%hu ", &s->summary[i]);
  fscanf(f, "\n");
}

void dostore_linkmin(FILE *f)  // minimal peer link information
{
  int i;
  
  for (i = 0; i < MAX_PEERLINKS; i++)
  {
    fprintf(f, "%d\n", peer_link[i].used);
    if (peer_link[i].used)
      fprintf(f, "%d %d\n", peer_link[i].id, peer_link[i].status);
  }
  fprintf(f, "\n");

  for (i = 0; i < EXTNEIGHBORHOODBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", extneighborhood_buffer[i].used);
    if (extneighborhood_buffer[i].used)
      fprintf(f, "%d %d\n", extneighborhood_buffer[i].neighbor_node,
        extneighborhood_buffer[i].extneighbor_node);
  }
  fprintf(f, "\n");
}

void doload_linkmin(FILE *f)
{
  int i;

  for (i = 0; i < MAX_PEERLINKS; i++)
  {
    fscanf(f, "%hhu\n", &peer_link[i].used);
    if (peer_link[i].used)
      fscanf(f, "%hu %hhu\n", &peer_link[i].id, &peer_link[i].status);
  }
  fscanf(f, "\n");

  for (i = 0; i < EXTNEIGHBORHOODBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &extneighborhood_buffer[i].used);
    if (extneighborhood_buffer[i].used)
      fscanf(f, "%hu %hu\n", &extneighborhood_buffer[i].neighbor_node,
        &extneighborhood_buffer[i].extneighbor_node);
  }
  fscanf(f, "\n");
}

void dostore_link(FILE *f)
{
  int i, j;

  fprintf(f, "%d %d\n", sendbroadcast_confirmseq, broadcast_msgindex);
  for (i = 0; i < MAX_PEERLINKS; i++)
  {
    fprintf(f, "%d\n", peer_link[i].used);
    if (peer_link[i].used)
    {
      fprintf(f, "%d %d %d\n", peer_link[i].id, peer_link[i].status, peer_link[i].response_time);
      fprintf(f, "%d %d %d %d\n", peer_link[i].sendunicast_confirmseq,
        peer_link[i].rcvunicast_confirmseq, peer_link[i].rcvbroadcast_confirmseq,
        peer_link[i].unicast_msgindex);
      fprintf(f, "%d %d\n", peer_link[i].rcv_power, peer_link[i].send_power);  
    }
  }

  for (i = 0; i < EXTNEIGHBORHOODBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", extneighborhood_buffer[i].used);
    if (extneighborhood_buffer[i].used)
      fprintf(f, "%d %d\n", extneighborhood_buffer[i].neighbor_node,
        extneighborhood_buffer[i].extneighbor_node);
  }
  fprintf(f, "\n");

  fprintf(f, "%d %d %d\n", outputbuffer_size, outputbuffer_sending, outputbuffer_maxindex);
  for (i = 0; i < OUTPUTBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", output_buffer[i].used);
    if (output_buffer[i].used)
    {
      fprintf(f, "%d\n", output_buffer[i].retry_count);
      fprintf(f, "%d %d\n", output_buffer[i].next, output_buffer[i].prev);
      fprintf(f, "%d %d %d\n", output_buffer[i].msg.source_id, output_buffer[i].msg.confirm_seq,
        output_buffer[i].msg.msg_len);

      for (j = 0; j < output_buffer[i].msg.msg_len; j++)
        fprintf(f, "%d ", output_buffer[i].msg.msg[j]);
      fprintf(f, "\n");

      fprintf(f, "%d ", output_buffer[i].node_count);
      for (j = 0; j < output_buffer[i].node_count; j++)
        fprintf(f, "%d ", output_buffer[i].nodes[j]);
      fprintf(f, "\n");
    }
  }

  fprintf(f, "\n");
  fprintf(f, "%d\n", outputschedule_size);
  for (i = 0; i < outputschedule_size; i++)
    fprintf(f, "%d %lu\n", output_schedule[i].outputbuffer_index, output_schedule[i].nexttry_time);
  fprintf(f, "\n");

  fprintf(f, "\n");
  fprintf(f, "%d\n", fragmentbuffer_size);
  for (i = 0; i < FRAGMENTBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", fragment_buffer[i].used);
    if (fragment_buffer[i].used)
    {
      fprintf(f, "%d %d %d\n", fragment_buffer[i].source_addr, fragment_buffer[i].is_broadcast,
        fragment_buffer[i].fragment_index);

      fprintf(f, "%d\n", fragment_buffer[i].msg_len);
      for (j = 0; j < fragment_buffer[i].msg_len; j++)
        fprintf(f, "%d ", fragment_buffer[i].msg[j]);
      fprintf(f, "\n");
    }
  }
  fprintf(f, "\n");
}

void doload_link(FILE *f)
{
  int i, j;

  fscanf(f, "%hhu %hd\n", &sendbroadcast_confirmseq, &broadcast_msgindex);
  for (i = 0; i < MAX_PEERLINKS; i++)
  {
    fscanf(f, "%hhu\n", &peer_link[i].used);
    if (peer_link[i].used)
    {
      fscanf(f, "%hu %hhu %hu\n", &peer_link[i].id, &peer_link[i].status, &peer_link[i].response_time);
      fscanf(f, "%hhu %hhu %hhu %hd\n", &peer_link[i].sendunicast_confirmseq,
        &peer_link[i].rcvunicast_confirmseq, &peer_link[i].rcvbroadcast_confirmseq,
        &peer_link[i].unicast_msgindex);
      fscanf(f, "%hhd %hhu\n", &peer_link[i].rcv_power, &peer_link[i].send_power);    
    }
  }

  for (i = 0; i < EXTNEIGHBORHOODBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &extneighborhood_buffer[i].used);
    if (extneighborhood_buffer[i].used)
      fscanf(f, "%hu %hu\n", &extneighborhood_buffer[i].neighbor_node,
        &extneighborhood_buffer[i].extneighbor_node);
  }
  fscanf(f, "\n");

  fscanf(f, "%hu %hhu %hu\n", &outputbuffer_size, &outputbuffer_sending, &outputbuffer_maxindex);
  for (i = 0; i < OUTPUTBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &output_buffer[i].used);
    if (output_buffer[i].used)
    {
      fscanf(f, "%hhu\n", &output_buffer[i].retry_count);
      fscanf(f, "%hd %hd\n", &output_buffer[i].next, &output_buffer[i].prev);
      fscanf(f, "%hu %hhu %hhu\n", &output_buffer[i].msg.source_id,
        &output_buffer[i].msg.confirm_seq, &output_buffer[i].msg.msg_len);

      output_buffer[i].msg.msg = (uint8_t *) mmalloc(output_buffer[i].msg.msg_len);
      for (j = 0; j < output_buffer[i].msg.msg_len; j++)
        fscanf(f, "%hhu ", &output_buffer[i].msg.msg[j]);
      fscanf(f, "\n");

      fscanf(f, "%hhu ", &output_buffer[i].node_count);
      if (output_buffer[i].node_count > 0)
      {
        output_buffer[i].nodes = (ADDRESS *) mcalloc(sizeof(ADDRESS), output_buffer[i].node_count);
        for (j = 0; j < output_buffer[i].node_count; j++)
          fscanf(f, "%hu ", &output_buffer[i].nodes[j]);
      } else output_buffer[i].nodes = NULL;
      fscanf(f, "\n");
    }
  }

  fscanf(f, "\n");
  fscanf(f, "%hu\n", &outputschedule_size);
  for (i = 0; i < outputschedule_size; i++)
    fscanf(f, "%hd %hu\n", &output_schedule[i].outputbuffer_index, &output_schedule[i].nexttry_time);
  fscanf(f, "\n");

  fscanf(f, "%hu\n", &fragmentbuffer_size);
  for (i = 0; i < FRAGMENTBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &fragment_buffer[i].used);
    if (fragment_buffer[i].used)
    {
      fscanf(f, "%hu %hhu %hhu\n", &fragment_buffer[i].source_addr,
        &fragment_buffer[i].is_broadcast, &fragment_buffer[i].fragment_index);

      fscanf(f, "%hhu\n", &fragment_buffer[i].msg_len);
      for (j = 0; j < fragment_buffer[i].msg_len; j++)
        fscanf(f, "%hhu ", &fragment_buffer[i].msg[j]);
      fscanf(f, "\n");
    }
  }
  fscanf(f, "\n");
}

void dostore_trees(FILE *f)
{
  int i, j, k;
  
  for (i = 0; i < TREE_COUNT; i++)
  {
    fprintf(f, "%d %d %d %d %d %d\n", trees[i].parent, trees[i].hops, trees[i].deepest_id,
      trees[i].deepest_hops, trees[i].deepestsum_id, trees[i].deepesthops_sum);
    for (k = 0; k < summarydesc_count; k++)
      summary_store(trees[i].summary[k], f);
    fprintf(f, "\n");
  }
  fprintf(f, "\n");

  for (i = 0; i < MAX_PEERLINKS; i++)
  {
    fprintf(f, "%d\n", peer_link[i].used);
    if (peer_link[i].used)
    {
      fprintf(f, "%d\n", peer_link[i].id);
      for (j = 0; j < TREE_COUNT; j++)
      {
        fprintf(f, "%d %d\n", peer_link[i].tree.trees[j].parent, peer_link[i].tree.trees[j].hops);

        fprintf(f, "%d\n", peer_link[i].tree.trees[j].deepest_hops);
        if (peer_link[i].tree.trees[j].deepest_hops > 0)
          for (k = 0; k < summarydesc_count; k++)
            summary_store(peer_link[i].tree.trees[j].summary[k], f);
      }
    }
    fprintf(f, "\n");
  }

  fprintf(f, "\n");
  for (i = 1; i < MAX_ATTRDESC; i++)
    if (attr_desc[i].is_dynamic == 0)
      fprintf(f, "%d ", attr_desc[i].value);
  fprintf(f, "\n");
}

void store_trees()
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), "%s/node%d.txt", CONFIG_TREEDIR, TOS_NODE_ID);
  f = file_open(s, "w");

  dostore_linkmin(f);
  dostore_trees(f);

  fclose(f);
}

void doload_trees(FILE *f)
{
  int i, j, k;
  ATTRIBUTE v;

  for (i = 0; i < TREE_COUNT; i++)
  {
    fscanf(f, "%hu %hhu %hu %hhu %hu %hhu\n", &trees[i].parent, &trees[i].hops, &trees[i].deepest_id,
      &trees[i].deepest_hops, &trees[i].deepestsum_id, &trees[i].deepesthops_sum);
    for (k = 0; k < summarydesc_count; k++)
      summary_load(trees[i].summary[k], f);
    fscanf(f, "\n");
  }
  fscanf(f, "\n");

  for (i = 0; i < MAX_PEERLINKS; i++)
  {
    fscanf(f, "%hhu\n", &peer_link[i].used);
    if (peer_link[i].used)
    {
      fscanf(f, "%hu\n", &peer_link[i].id);
      for (j = 0; j < TREE_COUNT; j++)
      {
        fscanf(f, "%hu %hhu\n", &peer_link[i].tree.trees[j].parent, &peer_link[i].tree.trees[j].hops);

        fscanf(f, "%hhu\n", &peer_link[i].tree.trees[j].deepest_hops);
        if (peer_link[i].tree.trees[j].deepest_hops > 0)
        {
          peer_link[i].tree.trees[j].summary = (SUMMARY **) mcalloc(sizeof(SUMMARY *), summarydesc_count);
          for (k = 0; k < summarydesc_count; k++)
          {
            peer_link[i].tree.trees[j].summary[k] = (SUMMARY *) mmalloc(sizeof(SUMMARY));
            summary_load(peer_link[i].tree.trees[j].summary[k], f);
          }
        }
      }
    }
    fscanf(f, "\n");
  }

  fscanf(f, "\n");
  for (i = 1; i < MAX_ATTRDESC; i++)
    if (attr_desc[i].is_dynamic == 0)
    {
      fscanf(f, "%hd ", &v);
      update_attr(i, v, 0);
    }
  
  fscanf(f, "\n");
}

void load_trees()
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), "%s/node%d.txt", CONFIG_TREEDIR, TOS_NODE_ID);
  f = file_open(s, "r");

  doload_linkmin(f);
  doload_trees(f);
  
  fclose(f);
}

void dostore_joinstate(FILE *f)
{
  int i, j;

  for (i = 0; i < QUERYJOINTARGETBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", queryjointarget_buffer[i].used);
    if (queryjointarget_buffer[i].used)
    {
      fprintf(f, "%d\n", queryjointarget_buffer[i].id);
      fprintf(f, "%d\n", queryjointarget_buffer[i].level);
      fprintf(f, "%d\n", queryjointarget_buffer[i].path_nodecount);
      if (queryjointarget_buffer[i].path_nodecount > 0)
      {
        for (j = 0; j < queryjointarget_buffer[i].path_nodecount; j++)
          fprintf(f, "%d ", queryjointarget_buffer[i].path_nodes[j]);
        fprintf(f, "\n");
      }
    }
  }

  fprintf(f, "\n");
  for (i = 0; i < QUERYSOURCEBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", querysource_buffer[i].used);
    if (querysource_buffer[i].used)
    {
      fprintf(f, "%d %d %d %lu %d %d %d %d %d\n", querysource_buffer[i].query_id,
        querysource_buffer[i].rel_id, querysource_buffer[i].id, querysource_buffer[i].pair_cost,
        querysource_buffer[i].jointarget_index, querysource_buffer[i].seq_number,
        querysource_buffer[i].sel[REL_S], querysource_buffer[i].sel[REL_T],
        querysource_buffer[i].sel[REL_J]);
      fprintf(f, "%d\n", querysource_buffer[i].path_len);
      if (querysource_buffer[i].path_len > 0)
      {
        for (j = 0; j < querysource_buffer[i].path_len; j++)
          fprintf(f, "%d ", querysource_buffer[i].path[j]);
        fprintf(f, "\n");
        fprintf(f, "%d ", querysource_buffer[i].path_levels != NULL);
        if (querysource_buffer[i].path_levels)
        {
          for (j = 0; j < querysource_buffer[i].path_len; j++)
            fprintf(f, "%d ", querysource_buffer[i].path_levels[j]);
          fprintf(f, "\n");
        }
      }
    }
  }

  fprintf(f, "\n");
  for (i = 0; i < MAX_JOINNODES; i++)
  {
    fprintf(f, "%d\n", windowjoin_nodes[i].used);
    if (windowjoin_nodes[i].used)
    {
      fprintf(f, "%d %d\n", windowjoin_nodes[i].node_id, windowjoin_nodes[i].path_len);
      for (j = 0; j < windowjoin_nodes[i].path_len; j++)
        fprintf(f, "%d ", windowjoin_nodes[i].path[j]);
      fprintf(f, "\n");
    }
  }

  fprintf(f, "\n");
  for (i = 0; i < MAX_JOINWINDOWS; i++)
  {
    fprintf(f, "%d\n", windowjoin_windows[i].used);
    if (windowjoin_windows[i].used)
      fprintf(f, "%d %d %d %d %d %d\n", windowjoin_windows[i].query_id, windowjoin_windows[i].rel_id,
        windowjoin_windows[i].max_seq, windowjoin_windows[i].width, windowjoin_windows[i].height,
        windowjoin_windows[i].node_index);
  }

  fprintf(f, "\n");
  for (i = 0; i < MAX_JOINPAIRS; i++)
  {
    fprintf(f, "%d\n", windowjoin_pairs[i].used);
    if (windowjoin_pairs[i].used)
      fprintf(f, "%d %d %d %d %d %d\n", windowjoin_pairs[i].window_index[REL_S],
        windowjoin_pairs[i].window_index[REL_T], windowjoin_pairs[i].sel_est[REL_S],
        windowjoin_pairs[i].sel_est[REL_T], windowjoin_pairs[i].sel_est[REL_J],
        windowjoin_pairs[i].options);
  }
  fprintf(f, "\n");

  for (i = 0; i < COSTBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", query_buffer[0].groupopt.cost_buffer[i].used);
    if (query_buffer[0].groupopt.cost_buffer[i].used)
      fprintf(f, "%d %d %d %d %d %d\n", query_buffer[0].groupopt.cost_buffer[i].node_id,
        query_buffer[0].groupopt.cost_buffer[i].source_id,
        query_buffer[0].groupopt.cost_buffer[i].cost.plancost_seq,
        query_buffer[0].groupopt.cost_buffer[i].cost.plancost_diff,
        query_buffer[0].groupopt.cost_buffer[i].decision.decision,
        query_buffer[0].groupopt.cost_buffer[i].decision.decision_seq);
    fprintf(f, "\n");
  }
  fprintf(f, "%d %d %d %d %d %d %d %d\n", query_buffer[0].groupopt.cost_updated,
    query_buffer[0].groupopt.coordinator_updated, query_buffer[0].groupopt.cost_received,
    query_buffer[0].groupopt.localcoordinator_index, query_buffer[0].groupopt.globalcoordinator_node,
    query_buffer[0].groupopt.decision, query_buffer[0].groupopt.rcv_decisionseq,
    query_buffer[0].groupopt.sent_decisionseq);

  fprintf(f, "\n");
}

void dostore_joinstatestats(FILE *f)
{
  int i, j;

  for (i = 0; i < QUERYJOINTARGETBUFFER_SIZE; i++)
    if (queryjointarget_buffer[i].used)
      for (j = 0; j < SRCREL_COUNT; j++)
        if (jointarget_multiplicity(&query_buffer[0], j, i) > 0)
          fprintf(f, "%d %d %d\n", queryjointarget_buffer[i].id, j,
            (queryjointarget_buffer[i].id == BASESTATION_ADDR) ?
            trees[0].hops : queryjointarget_buffer[i].path_nodecount);
}

void store_joinstate()
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), "%s/node%d.txt", CONFIG_JOINDIR, TOS_NODE_ID);
  f = file_open(s, "w");

  dostore_joinstate(f);
  fclose(f);

  snprintf(s, sizeof(s), "%s/node_stats%d.txt", CONFIG_JOINDIR, TOS_NODE_ID);
  f = file_open(s, "w");

  dostore_joinstatestats(f);
  fclose(f);
}

void doload_joinstate(FILE *f)
{
  int i, j;
  uint8_t t;

  for (i = 0; i < QUERYJOINTARGETBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &queryjointarget_buffer[i].used);
    if (queryjointarget_buffer[i].used)
    {
      fscanf(f, "%hu\n", &queryjointarget_buffer[i].id);
      fscanf(f, "%hhu\n", &queryjointarget_buffer[i].level);
      fscanf(f, "%hhu\n", &queryjointarget_buffer[i].path_nodecount);
      if (queryjointarget_buffer[i].path_nodecount > 0)
      {
        queryjointarget_buffer[i].path_nodes =
          (ADDRESS *) mcalloc(queryjointarget_buffer[i].path_nodecount, sizeof(ADDRESS));
        for (j = 0; j < queryjointarget_buffer[i].path_nodecount; j++)
          fscanf(f, "%hu ", &queryjointarget_buffer[i].path_nodes[j]);
        fscanf(f, "\n");
      }
    }
  }

  fscanf(f, "\n");
  for (i = 0; i < QUERYSOURCEBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &querysource_buffer[i].used);
    if (querysource_buffer[i].used)
    {
      fscanf(f, "%hhu %hhu %hu %lu %hhd %hhu %hhu %hhu %hhu\n", &querysource_buffer[i].query_id,
        &querysource_buffer[i].rel_id, &querysource_buffer[i].id,
        &querysource_buffer[i].pair_cost, &querysource_buffer[i].jointarget_index,
        &querysource_buffer[i].seq_number, &querysource_buffer[i].sel[REL_S],
        &querysource_buffer[i].sel[REL_T], &querysource_buffer[i].sel[REL_J]);
      fscanf(f, "%hhu\n", &querysource_buffer[i].path_len);
      if (querysource_buffer[i].path_len > 0)
      {
        querysource_buffer[i].path =
          (ADDRESS *) mcalloc(querysource_buffer[i].path_len, sizeof(ADDRESS));
        for (j = 0; j < querysource_buffer[i].path_len; j++)
          fscanf(f, "%hu ", &querysource_buffer[i].path[j]);
        fscanf(f, "\n");
        fscanf(f, "%hhu ", &t);
        if (t)
        {
          querysource_buffer[i].path_levels = (uint8_t *) mmalloc(querysource_buffer[i].path_len);
          for (j = 0; j < querysource_buffer[i].path_len; j++)
            fscanf(f, "%hhu ", &querysource_buffer[i].path_levels[j]);
          fscanf(f, "\n");
        }
      }
    }
  }

  fscanf(f, "\n");
  for (i = 0; i < MAX_JOINNODES; i++)
  {
    fscanf(f, "%hhu\n", &windowjoin_nodes[i].used);
    if (windowjoin_nodes[i].used)
    {
      fscanf(f, "%hu %hhu\n", &windowjoin_nodes[i].node_id, &windowjoin_nodes[i].path_len);
      if (windowjoin_nodes[i].path_len > 0)
      {
        windowjoin_nodes[i].path = (ADDRESS *) mcalloc(sizeof(ADDRESS), windowjoin_nodes[i].path_len);
        for (j = 0; j < windowjoin_nodes[i].path_len; j++)
          fscanf(f, "%hu ", &windowjoin_nodes[i].path[j]);
      }
      fscanf(f, "\n");
    }
  }

  fscanf(f, "\n");
  for (i = 0; i < MAX_JOINWINDOWS; i++)
  {
    fscanf(f, "%hhu\n", &windowjoin_windows[i].used);
    if (windowjoin_windows[i].used)
    {
      fscanf(f, "%hhu %hhu %hhu %hhu %hhu %hhu\n", &windowjoin_windows[i].query_id,
        &windowjoin_windows[i].rel_id, &windowjoin_windows[i].max_seq, &windowjoin_windows[i].width,
        &windowjoin_windows[i].height, &windowjoin_windows[i].node_index);
      windowjoin_windows[i].window = NULL;
    }
  }

  fscanf(f, "\n");
  for (i = 0; i < MAX_JOINPAIRS; i++)
  {
    fscanf(f, "%hhu\n", &windowjoin_pairs[i].used);
    if (windowjoin_pairs[i].used)
      fscanf(f, "%hhu %hhu %hhu %hhu %hhu %hhu\n", &windowjoin_pairs[i].window_index[REL_S],
        &windowjoin_pairs[i].window_index[REL_T], &windowjoin_pairs[i].sel_est[REL_S],
        &windowjoin_pairs[i].sel_est[REL_T], &windowjoin_pairs[i].sel_est[REL_J],
        &windowjoin_pairs[i].options);
  }
  fscanf(f, "\n");

  for (i = 0; i < COSTBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &query_buffer[0].groupopt.cost_buffer[i].used);
    if (query_buffer[0].groupopt.cost_buffer[i].used)
      fscanf(f, "%hu %hu %hhu %d %hhu %hhu\n", &query_buffer[0].groupopt.cost_buffer[i].node_id,
        &query_buffer[0].groupopt.cost_buffer[i].source_id,
        &query_buffer[0].groupopt.cost_buffer[i].cost.plancost_seq,
        &query_buffer[0].groupopt.cost_buffer[i].cost.plancost_diff,
        &query_buffer[0].groupopt.cost_buffer[i].decision.decision,
        &query_buffer[0].groupopt.cost_buffer[i].decision.decision_seq);
    fscanf(f, "\n");
  }
  fscanf(f, "%hhu %hhu %hhu %d %hu %hhu %hhu %hhu\n", &query_buffer[0].groupopt.cost_updated,
    &query_buffer[0].groupopt.coordinator_updated, &query_buffer[0].groupopt.cost_received,
    &query_buffer[0].groupopt.localcoordinator_index, &query_buffer[0].groupopt.globalcoordinator_node,
    &query_buffer[0].groupopt.decision, &query_buffer[0].groupopt.rcv_decisionseq,
    &query_buffer[0].groupopt.sent_decisionseq);

  fscanf(f, "\n");
}

void load_joinstate()
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), "%s/node%d.txt", CONFIG_JOINDIR, TOS_NODE_ID);
  f = file_open(s, "r");

  doload_joinstate(f);
  fclose(f);
}

void store_state()
{
  int i;
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), STATE_FILE, TOS_NODE_ID);
  f = file_open(s,"w");

  dostore_link(f);
  dostore_trees(f);

  fprintf(f, "\n");
  fprintf(f, "%d\n", routebuffer_size);
  for (i = 0; i < ROUTEBUFFER_SIZE; i++)
  {
    fprintf(f, "%d\n", route_buffer[i].used);
    if (route_buffer[i].used)
      fprintf(f, "%d %d %d %d\n", route_buffer[i].visited_type, route_buffer[i].route_id,
        route_buffer[i].source_node, route_buffer[i].min_cost);
  }

  dostore_joinstate(f);

  fprintf(f, "\n");
  fprintf(f, "%lu\n", timer_cycles);

  fclose(f);
}

void load_state()
{
  int i;
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), STATE_FILE, TOS_NODE_ID);
  f = file_open(s, "r");

  DBGOUT("Loading state from file %s\n", s);

  doload_link(f);
  doload_trees(f);
  
  fscanf(f, "\n");
  fscanf(f, "\n");
  fscanf(f, "%hhu\n", &routebuffer_size);
  for (i = 0; i < ROUTEBUFFER_SIZE; i++)
  {
    fscanf(f, "%hhu\n", &route_buffer[i].used);
    if (route_buffer[i].used)
      fscanf(f, "%hhu %hhu %hu %hhu\n", &route_buffer[i].visited_type, &route_buffer[i].route_id,
        &route_buffer[i].source_node, &route_buffer[i].min_cost);
  }

  doload_joinstate(f);

  fprintf(f, "\n");
  fscanf(f, "%lu\n", &timer_cycles);

  fclose(f);
}

void load_values()
{
  int i;
  FILE *f;
  char s[256];
  ATTRIBUTE v;

  snprintf(s, sizeof(s), "%s/node%d.txt", CONFIG_VALUEDIR, TOS_NODE_ID);
  f = file_open(s, "r");

  attr_desc[ATTR_ID].value = TOS_NODE_ID;
  for (i = 1; i < MAX_ATTRDESC; i++)
    if (attr_desc[i].is_dynamic == 0)
    {
      fscanf(f, "%hu ", &v);
      update_attr(i, v, 0);
    }

  fclose(f);
}

void route_request(ROUTE_MSG *msg)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), ROUTEREQUEST_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d\n", TOS_NODE_ID, msg->cmd == CMD_SEQROUTE, msg->route_id);

  fclose(f);
}

void finish_route(ROUTE_MSG *msg)
{
  char s[256];
  int i;
  FILE *f;

  snprintf(s, sizeof(s), ROUTECOMPLETED_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %lu\n", TOS_NODE_ID, msg->source_node, msg->route_id, msg->path_len-1,
    timer_cycles);
  fclose(f);

  snprintf(s, sizeof(s), ROUTESOURCEROUTE_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %lu %d %d %d %d %d\n", TOS_NODE_ID, timer_cycles, msg->route_id, msg->tree_index,
    msg->source_node, msg->top_index, msg->path_len-1);

  fprintf(f, "%d\n", msg->path_len);
  for (i = 0; i < msg->path_len; i++)
    fprintf(f, "%d ", msg->path[i]);
  fprintf(f, "\n");

  fclose(f);
}

void route_logunreachable(ROUTE_MSG *msg)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), ROUTEUNREACHABLE_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %lu\n", TOS_NODE_ID, msg->source_node, msg->route_id, msg->path_len-1,
    timer_cycles);
  fclose(f);
}

void do_logforward(FORWARD_MSG *msg, FILE *f)
{
  int i;

  fprintf(f, "%lu %d %d %d\n", timer_cycles, msg->source_node, msg->dest_node, msg->path_len);
  for (i = 0; i < msg->path_len; i++)
    fprintf(f, "%d ", msg->path[i]);
  fprintf(f, "\n");

  fprintf(f, "%d\n", msg->data_len);
  for (i = 0; i < msg->data_len; i++)
    fprintf(f, "%d ", msg->data[i]);
  fprintf(f, "\n");
}

void forward_request(FORWARD_MSG *msg)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), FORWARDREQUEST_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  do_logforward(msg, f);
  fclose(f);
}

void finish_forward(FORWARD_MSG *msg)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), FORWARDCOMPLETED_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  do_logforward(msg, f);
  fclose(f);
}

void forward_logunreachable(FORWARD_MSG *msg)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), FORWARDUNREACHABLE_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  do_logforward(msg, f);
  fclose(f);
}

void join_logpair(uint8_t query_id, ADDRESS source1, ADDRESS source2)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), JOINPAIR_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %lu\n", query_id, source1, source2, timer_cycles);
  fclose(f);
}

void join_lognode(uint8_t query_id, ADDRESS join_node, ADDRESS other_source)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), JOINNODE_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %lu\n", query_id, join_node, other_source, timer_cycles);
  fclose(f);
}

void join_logcoordcost(uint8_t query_id, ADDRESS coord_id, COORD_COST cost)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), COORDCOST_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %lu\n", query_id, coord_id, cost.plancost_seq, cost.plancost_diff,
    timer_cycles);
  fclose(f);
}

void join_logcoorddecision(uint8_t query_id, ADDRESS coord_id, COORD_DECISION decision)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), COORDDECISION_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %lu\n", query_id, coord_id, decision.decision_seq, decision.decision,
    timer_cycles);
  fclose(f);
}

void join_logselest(uint8_t query_id, ADDRESS src_node, ADDRESS target_node, uint8_t src_sel,
  uint8_t target_sel, uint8_t join_sel)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), SELEST_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %d %d %lu\n", query_id, src_node, target_node, src_sel, target_sel,
    join_sel, timer_cycles);
  fclose(f);
}

void log_querycostmodel(ADDRESS source_node, ADDRESS target_node, ADDRESS join_node, uint16_t cost)
{
  char s[256];
  FILE *f;

  snprintf(s, sizeof(s), QUERYJOINCOSTMODELLOG_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %lu\n", source_node, target_node, join_node, cost, timer_cycles);
  fclose(f);
}

void dolog_routestats(FILE *f)
{
  fprintf(f, "%d %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d\n",
    routebuffer_size, total_pathlen, msgsent_total, msgsent_route, 0L, msgsent_conf,
    msgsent_split, msgsent_splitforward, bytesent_total, bytesent_route, bytesent_forward,
    msgsent_forward, maxusage_mergebuffer, maxusage_outputbuffer, msgsent_extern, msgrcv_total,
    msgrcv_crcerror, msgrcv_route, msgrcv_forward, msgrcv_conf, msgrcv_split, bytercv_total,
    bytercv_route, bytercv_forward, bytesent_payload, bytercv_payload, bytesent_totalnoconf,
//    msggroup_sent, maxusage_group
#ifdef PERFORM_STACKTEST
    max_stackusage, heapman_maxusage
#else
    maxusage_payloadbuffer, heapman_maxusage
#endif
  );
}

void log_routestats()
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), ROUTESTATS_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "w");
  dolog_routestats(f);
  fclose(f);
}

void log_sendmsg(ADDRESS addr, uint8_t *msg, uint8_t len)
{
  int i;
  FILE *f;
  char s[256];

  f = file_open(MSGSENT_LOG, "a");

  fprintf(f, "%3d, %3lu: Sent message to %d: ", TOS_NODE_ID, timer_cycles, addr);
  for (i = 0; i < len; i++)
    fprintf(f, "%3d ", msg[i]);
  fprintf(f, "\n");

  fclose(f);

  snprintf(s, sizeof(s), MSGSENTID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%3d, %3lu: Sent message to %d: ", TOS_NODE_ID, timer_cycles, addr);
  for (i = 0; i < len; i++)
    fprintf(f, "%3d ", msg[i]);
  fprintf(f, "\n");

  fclose(f);
}

void log_rcvmsg(ADDRESS addr, uint8_t *msg, uint8_t len)
{
  int i;
  FILE *f;
  char s[256];

  f = file_open(MSGRCV_LOG, "a");

  fprintf(f, "%3d, %3lu: Received message from %d: ", TOS_NODE_ID, timer_cycles, addr);
  for (i = 0; i < len; i++)
    fprintf(f, "%3hhu ", msg[i]);
  fprintf(f, "\n");

  fclose(f);

  snprintf(s, sizeof(s), MSGRCVID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%3d, %3lu: Received message from %d: ", TOS_NODE_ID, timer_cycles, addr);
  for (i = 0; i < len; i++)
    fprintf(f, "%3hhu ", msg[i]);
  fprintf(f, "\n");

  fclose(f);
}

void dolog_sendcmd(FILE *f, char *label, uint8_t node_count, ADDRESS *nodes, uint8_t *cmd,
  uint8_t len)
{
  int i;

  fprintf(f, "%3d, %3lu: %s", TOS_NODE_ID, timer_cycles, label);
  for (i = 0; i < node_count; i++)
    fprintf(f, " %d", nodes[i]);
  fprintf(f, ": ");

  for (i = 0; i < len; i++)
    fprintf(f, "%3d ", cmd[i]);
  fprintf(f, "\n");
}

void log_sendcmd(uint8_t node_count, ADDRESS *nodes, uint8_t *cmd, uint8_t len)
{
  FILE *f;
  char s[256];

  f = file_open(CMDSENT_LOG, "a");
  dolog_sendcmd(f, "Sent command to", node_count, nodes, cmd, len);
  fclose(f);

  snprintf(s, sizeof(s), CMDSENTID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");
  dolog_sendcmd(f, "Sent command to", node_count, nodes, cmd, len);
  fclose(f);
}

void log_sendcmdconf(uint8_t node_count, ADDRESS *nodes, uint8_t *cmd, uint8_t len, uint8_t success)
{
  FILE *f;
  char s[256];

  f = file_open(CMDSENTCONF_LOG, "a");
  dolog_sendcmd(f, success ? "Successfully sent command to" : "Unsucessfully send command to",
    node_count, nodes, cmd, len);
  fclose(f);

  snprintf(s, sizeof(s), CMDSENTCONFID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");
  dolog_sendcmd(f, success ? "Successfully sent command to" : "Unsucessfully send command to",
    node_count, nodes, cmd, len);
  fclose(f);
}

void log_rcvcmd(ADDRESS addr, uint8_t *cmd, uint8_t len)
{
  int i;
  FILE *f;
  char s[256];

  f = file_open(CMDRCV_LOG, "a");

  fprintf(f, "%3d, %3lu: Received command from %d: ", TOS_NODE_ID, timer_cycles, addr);
  for (i = 0; i < len; i++)
    fprintf(f, "%3hhu ", cmd[i]);
  fprintf(f, "\n");

  fclose(f);

  snprintf(s, sizeof(s), CMDRCVID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%3d, %3lu: Received command: ", TOS_NODE_ID, timer_cycles);
  for (i = 0; i < len; i++)
    fprintf(f, "%3hhu ", cmd[i]);
  fprintf(f, "\n");

  fclose(f);
}

void log_dropcmd(uint8_t node_count, ADDRESS *nodes, uint8_t *cmd, uint8_t len, uint8_t code)
{
  FILE *f;
  char s[256], s1[256];

  snprintf(s1, sizeof(s1), "Dropped command, code %d", code);
  f = file_open(CMDDROP_LOG, "a");
  dolog_sendcmd(f, s1, node_count, nodes, cmd, len);
  fclose(f);

  snprintf(s, sizeof(s), CMDDROPID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");
  dolog_sendcmd(f, s1, node_count, nodes, cmd, len);
  fclose(f);

  if (config.confirm_drop)
  {
    if (config.confirm_drop & 0x02)
      exit(1);
    pause_sim();
  }
}

void log_sendcmdhi(uint8_t *cmd, uint8_t len)
{
  int i;
  FILE *f;
  char s[256];

  f = file_open(CMDHISENT_LOG, "a");

  fprintf(f, "%3d, %3lu: Sent command: ", TOS_NODE_ID, timer_cycles);
  for (i = 0; i < len; i++)
    fprintf(f, "%3d ", cmd[i]);
  fprintf(f, "\n");

  fclose(f);

  snprintf(s, sizeof(s), CMDHISENTID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%3d, %3lu: Sent command: ", TOS_NODE_ID, timer_cycles);
  for (i = 0; i < len; i++)
    fprintf(f, "%3d ", cmd[i]);
  fprintf(f, "\n");

  fclose(f);
}

void log_rcvcmdhi(uint8_t *cmd, uint8_t len)
{
  int i;
  FILE *f;
  char s[256];

  f = file_open(CMDHIRCV_LOG, "a");

  fprintf(f, "%3d, %3lu: Received command: ", TOS_NODE_ID, timer_cycles);
  for (i = 0; i < len; i++)
    fprintf(f, "%3hhu ", cmd[i]);
  fprintf(f, "\n");

  fclose(f);

  snprintf(s, sizeof(s), CMDHIRCVID_LOG, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%3d, %3lu: Received command: ", TOS_NODE_ID, timer_cycles);
  for (i = 0; i < len; i++)
    fprintf(f, "%3hhu ", cmd[i]);
  fprintf(f, "\n");

  fclose(f);
}

void log_querysample(uint8_t attr, ATTRIBUTE v)
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), QUERYSAMPLELOG_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%ld %d %d\n", timer_cycles, attr, v);
  fclose(f);
}

void log_tuple(FILE *f, uint8_t tuple_len, ATTRIBUTE *tuple)
{
  int i;

  for (i = 0; i < tuple_len; i++)
    fprintf(f, "%d ", tuple[i]);
  fprintf(f, "\n");
}

void log_queryjoinin(ADDRESS source_node, uint8_t tuple_len, ATTRIBUTE *tuple)
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), QUERYJOININLOG_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%lu %d %d ", timer_cycles, source_node, TUPLEGET_QUERY(tuple));
  log_tuple(f, tuple_len, tuple);

  fclose(f);
}

void log_queryjoinout(ADDRESS dest_node, uint8_t tuple_len, ATTRIBUTE *tuple)
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), QUERYJOINOUTLOG_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%lu %d %d ", timer_cycles, dest_node, TUPLEGET_QUERY(tuple));
  log_tuple(f, tuple_len, tuple);

  fclose(f);
}

void log_multicastrequest(uint8_t node_count, ADDRESS *nodes, uint8_t multicastbuffer_len,
  uint8_t *multicast_buffer)
{
  FILE *f;
  int i;
  char s[256];
  
  snprintf(s, sizeof(s), MULTICASTREQUEST_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");

  fprintf(f, "%d\n", TOS_NODE_ID);
  fprintf(f, "%d\n", node_count);
  for (i = 0; i < node_count; i++)
    fprintf(f, "%d ", nodes[i]);
  fprintf(f, "\n");

  fprintf(f, "%d\n", multicastbuffer_len);
  for (i = 0; i < multicastbuffer_len; i++)
    fprintf(f, "%d ", multicast_buffer[i]);
  fprintf(f, "\n\n");

  fclose(f);
}

void log_multicastcompleted(ADDRESS source_node)
{
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), MULTICASTCOMPLETED_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d\n", TOS_NODE_ID, source_node);

  fclose(f);
}

void log_repairforward(uint8_t success, ADDRESS source_node, ADDRESS dest_node,
  ADDRESS old_neighbornode, ADDRESS new_neighbornode, int8_t len_diff, ADDRESS *old_path,
  uint8_t old_pathlen, ADDRESS *new_path, uint8_t new_pathlen)
{
  int i;
  FILE *f;
  char s[256];

  snprintf(s, sizeof(s), REPAIRFORWARD_FILE, CONFIG_ROUTEDIR, TOS_NODE_ID);
  f = file_open(s, "a");
  fprintf(f, "%d %d %d %d %d %d\n", success, source_node, dest_node, old_neighbornode,
    new_neighbornode, len_diff);

  fprintf(f, "%d\n", old_pathlen);
  for (i = 0; i < old_pathlen; i++)
    fprintf(f, "%d ", old_path[i]);
  fprintf(f, "\n");

  fprintf(f, "%d\n", new_pathlen);
  for (i = 0; i < new_pathlen; i++)
    fprintf(f, "%d ", new_path[i]);
  fprintf(f, "\n");

  fclose(f);
}
#endif
